import { JobResultNotification, JobStatus } from '../type';
import { StoragePath } from '../simulationStoragePath';
import { SimulationPath } from '../simulationPath';
import { SimulationLogger } from '../simulationLogger';
import { SimulationJobRunner } from '../simulationJobRunner';
import { SimulationJobManager } from '../simulationJobManager';
import { SimulationParameterService } from '../service/parameterService';
import { reportService } from '../service/reportService';
export interface ISimulationJobState {
  /**
   * Execution entry for each simulation state
   */
  execute(): Promise<void>;

  /**
   *  Interrupt the simulation job in any job state
   */
  cancel(): Promise<void>;
}
/**
 * Job states are there to show which stage a job has reached
 */
export abstract class BaseSimulationJobState implements ISimulationJobState {

  public name: string;
  public jobId: string;
  public product: string;
  
  public context: SimulationJobRunner;
  public logger: SimulationLogger;
  public simJobRepo = SimulationJobManager.Instance;
  public storagePath: StoragePath;
  public simulationPath: SimulationPath;
  public simParameterService: SimulationParameterService;

  constructor(stateName: string) {
    this.name = stateName;
  }
  
  public setContext(context: SimulationJobRunner) {
    this.context = context;
  }

  public getStateName() {
    return this.name;
  }

  protected async doNextStateOperation(state: BaseSimulationJobState) {
    this.context.transitionTo(state);
    this.logger.info(`🍺 ==> Context(jobId=${this.context?.jobId}): Transition to ${state.constructor.name}`);
    await this.context.execute();
  }

  private isRunnable() {
    return this.simJobRepo.getJobStatus(this.jobId) !== JobStatus.Interrupted;
  }

  async execute() {
    if (this.isRunnable()) {
      this.jobId = this.context.jobId;
      this.product = this.context.product;
      this.logger = this.context.logger;
      this.storagePath = this.context.storagePath;
      this.simulationPath = this.context.simulationPath;
      this.simParameterService = this.context.simulationParameterService;
      await this.doRun();
    } else {
      this.logger.debug(`🐬 Agent detected that the simulation job status is interrupted ==> current state: ${this.context.getCurrentStateName()}`);
    }
  }

  async cancel() {
    this.logger.debug(`⏹️ Interrupt the simulation job(jobId=${this.jobId}) immediately`);
    this.simJobRepo.updateJobStatus(this.jobId, JobStatus.Interrupted);
    const jobResult: JobResultNotification = {
      id: this.jobId,
      success: false,
      message: "timeout",
      result: null
    };
    await reportService.jobResultReport(jobResult);
    await this.doCancel();
  }

  /**
   * sub-classes implement this method to perform specific state behavior
   * @param args 
   */
  protected abstract doRun(): Promise<void>;

  /**
   * sub-classes can override this method
   */
  protected async doCancel() { }
}